package com.github.mikephil.charting.custom.linechart;

import android.content.Context;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.util.Log;

import com.github.mikephil.charting.data.DataSet;
import com.github.mikephil.charting.data.LineDataSet;
import com.github.mikephil.charting.data.LineRadarDataSet;
import com.github.mikephil.charting.utils.ColorTemplate;
import com.github.mikephil.charting.utils.Utils;

import java.util.ArrayList;
import java.util.List;

import androidx.annotation.Nullable;

/**
 * @author Created by 汪高皖 on 2018/4/13 0013 14:35
 */
public class SmartLineDataSet extends LineRadarDataSet<SmartLineEntry> implements ISmartLineDataSet {
    /**
     * Drawing mode for this line dataset
     **/
    private LineDataSet.Mode mMode = LineDataSet.Mode.LINEAR;
    
    /**
     * List representing all colors that are used for the circles
     */
    private List<Integer> mCircleColors = null;
    
    private float[] mColorsPercent = null;
    
    /**
     * the color of the inner circles
     */
    private int mCircleColorHole = Color.WHITE;
    
    /**
     * the radius of the circle-shaped value indicators
     */
    private float mCircleRadius = 8f;
    
    /**
     * the hole radius of the circle-shaped value indicators
     */
    private float mCircleHoleRadius = 4f;
    
    /**
     * sets the intensity of the cubic lines
     */
    private float mCubicIntensity = 0.2f;
    
    /**
     * the path effect of this DataSet that makes dashed lines possible
     */
    private DashPathEffect mDashPathEffect = null;
    
    /**
     * formatter for customizing the position of the fill-line
     */
    private ISmartFillFormatter mFillFormatter = new DefaultFillFormatter();
    
    /**
     * if true, drawing circles is enabled
     */
    private boolean mDrawCircles = true;
    
    private boolean mDrawCircleHole = true;
    
    public SmartLineDataSet(List<SmartLineEntry> yVals, String label) {
        super(yVals, label);
        
        if (mCircleColors == null) {
            mCircleColors = new ArrayList<>();
        }
        mCircleColors.clear();
        mCircleColors.add(Color.rgb(140, 234, 255));
    }
    
    @Override
    public DataSet<SmartLineEntry> copy() {
        
        List<SmartLineEntry> yVals = new ArrayList<>();
        
        for (int i = 0; i < mValues.size(); i++) {
            yVals.add(mValues.get(i).copy());
        }
        
        SmartLineDataSet copied = new SmartLineDataSet(yVals, getLabel());
        copied.mMode = mMode;
        copied.mColors = mColors;
        copied.mCircleRadius = mCircleRadius;
        copied.mCircleHoleRadius = mCircleHoleRadius;
        copied.mCircleColors = mCircleColors;
        copied.mDashPathEffect = mDashPathEffect;
        copied.mDrawCircles = mDrawCircles;
        copied.mDrawCircleHole = mDrawCircleHole;
        copied.mHighLightColor = mHighLightColor;
        
        return copied;
    }
    
    /**
     * Returns the drawing mode for this line dataset
     */
    @Override
    public LineDataSet.Mode getMode() {
        return mMode;
    }
    
    /**
     * Returns the drawing mode for this LineDataSet
     */
    public void setMode(LineDataSet.Mode mode) {
        mMode = mode;
    }
    
    /**
     * Sets the intensity for cubic lines (if enabled). Max = 1f = very cubic,
     * Min = 0.05f = low cubic effect, Default: 0.2f
     */
    public void setCubicIntensity(float intensity) {
        
        if (intensity > 1f) {
            intensity = 1f;
        }
        if (intensity < 0.05f) {
            intensity = 0.05f;
        }
        
        mCubicIntensity = intensity;
    }
    
    
    @Override
    public float getCubicIntensity() {
        return mCubicIntensity;
    }
    
    
    /**
     * Sets the radius of the drawn circles.
     * Default radius = 4f, Min = 1f
     */
    public void setCircleRadius(float radius) {
        
        if (radius >= 1f) {
            mCircleRadius = Utils.convertDpToPixel(radius);
        } else {
            Log.e("LineDataSet", "Circle radius cannot be < 1");
        }
    }
    
    /**
     * 设置外圆的半径，单位px
     *
     * @param radius px
     */
    public void setCircleRadiusInPx(float radius) {
        if (radius >= 1f) {
            mCircleRadius = radius;
        } else {
            Log.e("LineDataSet", "Circle radius cannot be < 1");
        }
    }
    
    @Override
    public float getCircleRadius() {
        return mCircleRadius;
    }
    
    /**
     * Sets the hole radius of the drawn circles.
     * Default radius = 2f, Min = 0.5f
     */
    public void setCircleHoleRadius(float holeRadius) {
        
        if (holeRadius >= 0.5f) {
            mCircleHoleRadius = Utils.convertDpToPixel(holeRadius);
        } else {
            Log.e("LineDataSet", "Circle radius cannot be < 0.5");
        }
    }
    
    /**
     * 设置内圆的半径，单位px
     *
     * @param holeRadius px
     */
    public void setCircleHoleRadiusInPx(float holeRadius) {
        if (holeRadius >= 0.5f) {
            mCircleHoleRadius = holeRadius;
        } else {
            Log.e("LineDataSet", "Circle radius cannot be < 0.5");
        }
    }
    
    @Override
    public float getCircleHoleRadius() {
        return mCircleHoleRadius;
    }
    
    /**
     * sets the size (radius) of the circle shpaed value indicators,
     * default size = 4f
     * <p/>
     * This method is deprecated because of unclarity. Use setCircleRadius instead.
     */
    @Deprecated
    public void setCircleSize(float size) {
        setCircleRadius(size);
    }
    
    /**
     * This function is deprecated because of unclarity. Use getCircleRadius instead.
     */
    @Deprecated
    public float getCircleSize() {
        return getCircleRadius();
    }
    
    /**
     * Enables the line to be drawn in dashed mode, e.g. like this
     * "- - - - - -". THIS ONLY WORKS IF HARDWARE-ACCELERATION IS TURNED OFF.
     * Keep in mind that hardware acceleration boosts performance.
     *
     * @param lineLength  the length of the line pieces
     * @param spaceLength the length of space in between the pieces
     * @param phase       offset, in degrees (normally, use 0)
     */
    public void enableDashedLine(float lineLength, float spaceLength, float phase) {
        mDashPathEffect = new DashPathEffect(new float[]{
            lineLength, spaceLength
        }, phase);
    }
    
    /**
     * Disables the line to be drawn in dashed mode.
     */
    public void disableDashedLine() {
        mDashPathEffect = null;
    }
    
    @Override
    public boolean isDashedLineEnabled() {
        return mDashPathEffect == null ? false : true;
    }
    
    @Override
    public DashPathEffect getDashPathEffect() {
        return mDashPathEffect;
    }
    
    /**
     * set this to true to enable the drawing of circle indicators for this
     * DataSet, default true
     */
    public void setDrawCircles(boolean enabled) {
        this.mDrawCircles = enabled;
    }
    
    @Override
    public boolean isDrawCirclesEnabled() {
        return mDrawCircles;
    }
    
    @Deprecated
    @Override
    public boolean isDrawCubicEnabled() {
        return mMode == LineDataSet.Mode.CUBIC_BEZIER;
    }
    
    @Deprecated
    @Override
    public boolean isDrawSteppedEnabled() {
        return mMode == LineDataSet.Mode.STEPPED;
    }
    
    /** ALL CODE BELOW RELATED TO CIRCLE-COLORS */
    
    /**
     * returns all colors specified for the circles
     */
    public List<Integer> getCircleColors() {
        return mCircleColors;
    }
    
    /**
     * 设置渐变色各颜色所占比重，可设置为NULL，此时所有颜色所占比重相同，如果不为NULL，则Size必须和颜色值相同
     */
    public void setColorsPercents(@Nullable float[] colorsPercent) {
        mColorsPercent = colorsPercent;
    }
    
    /**
     * 设置渐变色各颜色所占比重，可设置为NULL，此时所有颜色所占比重相同，如果不为NULL，则Size必须和颜色值相同
     */
    public void setColorsPercent(@Nullable float... colorsPercent) {
        mColorsPercent = colorsPercent;
    }
    
    @Override
    public float[] getColorsPercent() {
        return mColorsPercent;
    }
    
    @Override
    public int getCircleColor(int index) {
        return mCircleColors.get(index);
    }
    
    @Override
    public int getCircleColorCount() {
        return mCircleColors.size();
    }
    
    /**
     * Sets the colors that should be used for the circles of this DataSet.
     * Colors are reused as soon as the number of Entries the DataSet represents
     * is higher than the size of the colors array. Make sure that the colors
     * are already prepared (by calling getResources().getColor(...)) before
     * adding them to the DataSet.
     */
    public void setCircleColors(List<Integer> colors) {
        mCircleColors = colors;
    }
    
    /**
     * Sets the colors that should be used for the circles of this DataSet.
     * Colors are reused as soon as the number of Entries the DataSet represents
     * is higher than the size of the colors array. Make sure that the colors
     * are already prepared (by calling getResources().getColor(...)) before
     * adding them to the DataSet.
     */
    public void setCircleColors(int... colors) {
        this.mCircleColors = ColorTemplate.createColors(colors);
    }
    
    /**
     * ets the colors that should be used for the circles of this DataSet.
     * Colors are reused as soon as the number of Entries the DataSet represents
     * is higher than the size of the colors array. You can use
     * "new String[] { R.color.red, R.color.green, ... }" to provide colors for
     * this method. Internally, the colors are resolved using
     * getResources().getColor(...)
     */
    public void setCircleColors(int[] colors, Context c) {
        
        List<Integer> clrs = mCircleColors;
        if (clrs == null) {
            clrs = new ArrayList<>();
        }
        clrs.clear();
        
        for (int color : colors) {
            clrs.add(c.getResources().getColor(color));
        }
        
        mCircleColors = clrs;
    }
    
    /**
     * Sets the one and ONLY color that should be used for this DataSet.
     * Internally, this recreates the colors array and adds the specified color.
     */
    public void setCircleColor(int color) {
        resetCircleColors();
        mCircleColors.add(color);
    }
    
    /**
     * resets the circle-colors array and creates a new one
     */
    public void resetCircleColors() {
        if (mCircleColors == null) {
            mCircleColors = new ArrayList<Integer>();
        }
        mCircleColors.clear();
    }
    
    /**
     * Sets the color of the inner circle of the line-circles.
     */
    public void setCircleColorHole(int color) {
        mCircleColorHole = color;
    }
    
    @Override
    public int getCircleHoleColor() {
        return mCircleColorHole;
    }
    
    /**
     * Set this to true to allow drawing a hole in each data circle.
     */
    public void setDrawCircleHole(boolean enabled) {
        mDrawCircleHole = enabled;
    }
    
    @Override
    public boolean isDrawCircleHoleEnabled() {
        return mDrawCircleHole;
    }
    
    /**
     * Sets a custom IFillFormatter to the chart that handles the position of the
     * filled-line for each DataSet. Set this to null to use the default logic.
     */
    public void setFillFormatter(ISmartFillFormatter formatter) {
        
        if (formatter == null) {
            mFillFormatter = new DefaultFillFormatter();
        } else {
            mFillFormatter = formatter;
        }
    }
    
    @Override
    public ISmartFillFormatter getFillFormatter() {
        return mFillFormatter;
    }
    
    private class DefaultFillFormatter implements ISmartFillFormatter {
        
        @Override
        public float getFillLinePosition(ISmartLineDataSet dataSet, SmartLineDataProvider dataProvider) {
            
            float fillMin = 0f;
            float chartMaxY = dataProvider.getYChartMax();
            float chartMinY = dataProvider.getYChartMin();
            
            SmartLineData data = dataProvider.getLineData();
            
            if (dataSet.getYMax() > 0 && dataSet.getYMin() < 0) {
                fillMin = 0f;
            } else {
                
                float max, min;
                
                if (data.getYMax() > 0) {
                    max = 0f;
                } else {
                    max = chartMaxY;
                }
                if (data.getYMin() < 0) {
                    min = 0f;
                } else {
                    min = chartMinY;
                }
                
                fillMin = dataSet.getYMin() >= 0 ? min : max;
            }
            
            return fillMin;
        }
    }
}
